<?php
/**
 * @author 595949289@qq.com
 * @datetime 2022/9/17 15:01
 */

namespace CuiFox\AliOss;

use Yii;
use OSS\OssClient;
use yii\base\Component;
use OSS\Core\OssException;
use yii\base\InvalidConfigException;

class Client extends Component
{
    /**
     * @var string AccessKeyID
     */
    public $accessKeyId;
    /**
     * @var string AccessKeySecret
     */
    public $accessKeySecret;
    /**
     * @var string Bucket
     */
    public $bucket;
    /**
     * @var string OSS外网地址
     */
    public $wanEndpoint;
    /**
     * @var string OSS内网地址
     */
    public $lanEndpoint;
    /**
     * @var bool 上传文件是否使用内网，免流量费
     */
    public $isInternal = false;
    /**
     * @var string 从wanEndpoint和lanEndpoint中选取, 默认外网
     */
    protected $endpoint;
    /**
     * @var OssClient $client
     */
    private $client;
    /**
     * @var bool 是否私有空间, 默认公开空间
     */
    public $private = false;
    /**
     * @var string baseUrl
     */
    public $baseUrl;

    /**
     * @throws InvalidConfigException
     */
    public function init()
    {
        if ($this->accessKeyId === null) {
            throw new InvalidConfigException('The "accessKeyId" property must be set.');
        } elseif ($this->accessKeySecret === null) {
            throw new InvalidConfigException('The "accessKeySecret" property must be set.');
        } elseif ($this->bucket === null) {
            throw new InvalidConfigException('The "bucket" property must be set.');
        } elseif ($this->lanEndpoint === null) {
            throw new InvalidConfigException('The "lanEndpoint" property must be set.');
        } elseif ($this->wanEndpoint === null) {
            throw new InvalidConfigException('The "wanEndpoint" property must be set.');
        }
        $this->endpoint = $this->isInternal ? $this->lanEndpoint : $this->wanEndpoint;
    }

    /**
     * 获取Endpoint
     * @return string
     */
    public function getEndpoint()
    {
        return $this->endpoint;
    }

    /**
     * 获取请求域名
     * @param bool $secure
     * @return string
     */
    public function getHost($secure = true)
    {
        return ($secure ? 'https://' : 'http://') . $this->bucket . '.' . $this->endpoint;
    }

    /**
     * 获取OssClient
     * @return mixed
     */
    public function getClient()
    {
        if ($this->client === null) {
            $this->setClient(new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint));
        }
        return $this->client;
    }

    /**
     * 设置OssClient
     * @param OssClient $client
     */
    public function setClient(OssClient $client)
    {
        $this->client = $client;
    }

    /**
     * Object是否存在
     * @param $object
     * @return bool
     */
    public function has($object)
    {
        try {
            return $this->getClient()->doesObjectExist($this->bucket, $object);
        } catch (OssException $e) {
            Yii::info($e->getMessage());
            return false;
        }
    }

    /**
     * 读取Object
     * @param $object
     * @return array|bool
     */
    public function read($object)
    {
        if (!($resource = $this->readStream($object))) {
            return false;
        }
        $resource['contents'] = stream_get_contents($resource['stream']);
        fclose($resource['stream']);
        unset($resource['stream']);
        return $resource;
    }

    /**
     * @param $object
     * @return array|bool
     */
    public function readStream($object)
    {
        $url = $this->getClient()->signUrl($this->bucket, $object, 3600);
        $stream = fopen($url, 'r');
        if (!$stream) {
            return false;
        }
        return compact('stream', 'object');
    }

    /**
     * 上传
     * @param $object
     * @param $file
     * @param null $options
     * @return bool|null
     */
    public function upload($object, $file, $options = null)
    {
        try {
            return $this->getClient()->uploadFile($this->bucket, $object, $file, $options);
        } catch (OssException $e) {
            Yii::info($e->getMessage());
            return false;
        }
    }

    /**
     * 删除
     * @param $object
     * @param null $options
     * @return bool
     */
    public function delete($object, $options = null)
    {
        try {
            return $this->getClient()->deleteObject($this->bucket, $object, $options) === null;
        } catch (OssException $e) {
            Yii::info($e->getMessage());
            return false;
        }
    }

    /**
     * 创建文件夹
     * @param $object
     * @return bool|null
     */
    public function createDir($object)
    {
        try {
            return $this->getClient()->createObjectDir($this->bucket, rtrim($object, '/'));
        } catch (OssException $e) {
            Yii::info($e->getMessage());
            return false;
        }
    }

    /**
     * @param $object
     * @param int $timeout
     * @param string $method
     * @param null $options
     * @return string
     */
    public function getUrl($object, $timeout = 60, $method = OssClient::OSS_HTTP_GET, $options = null)
    {
        if ($this->private) {
            return $this->getClient()->signUrl($this->bucket, $object, $timeout, $method, $options);
        } else {
            return $this->baseUrl ? $this->baseUrl . $object : $this->endpoint . $object;
        }
    }

    /**
     * 下载
     * @param $object
     * @param $file
     * @return bool|string
     */
    public function download($object, $file)
    {
        try {
            $options = array(
                OssClient::OSS_FILE_DOWNLOAD => $file,
            );
            return $this->getClient()->getObject($this->bucket, $object, $options);
        } catch (OssException $e) {
            Yii::info($e->getMessage());
            return false;
        }
    }

    /**
     * 获取所有对象
     * @param array $options = [
     *      'max-keys'  => max-keys用于限定此次返回object的最大数，如果不设定，默认为100，max-keys取值不能大于1000。
     *      'prefix'    => 限定返回的object key必须以prefix作为前缀。注意使用prefix查询时，返回的key中仍会包含prefix。
     *      'delimiter' => 是一个用于对Object名字进行分组的字符。所有名字包含指定的前缀且第一次出现delimiter字符之间的object作为一组元素
     *      'marker'    => 用户设定结果从marker之后按字母排序的第一个开始返回。
     * ]
     * @return array
     */
    public function getAllObject($options = [])
    {
        $objects = $this->getClient()->listObjects($this->bucket, $options);
        $list = [];
        foreach ($objects->getObjectList() as $object) {
            $list[] = $object->getKey();
        }
        return $list;
    }
}